#  Copyright (c) 2022. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
#  Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan.
#  Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna.
#  Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus.
#  Vestibulum commodo. Ut rhoncus gravida arcu.

# coding:utf8
"""
装集合的容器

"""
from typing import List, Union

from ..._pack.ip import IpState
from log import logger
from copy import deepcopy


class ExceptInsertFull(Exception):
    pass


class ExceptInsertNoEqualSection(Exception):
    pass
    
    
class __Text:
    def __init__(self, val):
        self.val = val
        pass
    
    def int(self):
        return self.__class__(int(self.val))
    
    def str(self):
        return self.__class__(str(self.val))
    
    def value(self):
        return self.val
    
    
        
        
    
    
    
class Co_ntainer(object):
    """
    横向容器 容纳10个对象
    """
    size_max = 10

    def __init__(self):
        """ """
        self.section = ""
        self.list: List[IpState] = []  # 里面是相同的字段的IP
        self.pos = 0

    def add(self, input_ipoat: IpState):
        """
        默认按顺序排序 从小到大

        """
        section_input = input_ipoat.section()

        # *****************************如果列表为空
        if not self.list:
            if not self.section:
                self.list.append(input_ipoat)
                self.section = section_input
                return 0  # 索引位置
            else:
                if section_input == self.section:
                    self.list.append(input_ipoat)
                    return 0
                else:
                    # 要添加的第一个元素字段不一致,添加失败
                    raise ExceptInsertNoEqualSection("字段不一致")
                    # return None
                
        # ************************************* 添加的元素的字段不一致
        if self.section != section_input:  # 添加的元素的字段不一致
            raise ExceptInsertNoEqualSection("插入的元素字段与当前co不一致")
            # return None

        # ************************************* 列表已经满,不能再插入
        if self.isfull():
            raise ExceptInsertFull("列表已经满,不能再插入")
            # raise ValueError("列表已经满,不能再插入")

        # *************************************
        tail = int(input_ipoat.tail())
        for ipo_ in self.list:
            tail_ = int(ipo_.tail())
            if tail <= tail_:  # 如果要插入的尾部 <= 现在的对象尾部, 立即插入到现在对象的前面,否则
                index_ = self.list.index(ipo_)
                self.list.insert(index_, input_ipoat)
                # return self.list.index(ipoat)  # todo 可以修改更简单的方法
                return index_
            else:
                continue
                
        self.list.append(input_ipoat)
        return len(self.list) - 1

    def isfull(self):
        return True if len(self.list) == self.size_max else False

    def set_section(self, section):
        self.section = section

    def get_section(self):
        return self.section

    def __str__(self):
        eles = ','.join([ipo.get_ip() for ipo in self.list])
        tsr = "<Container> [{}]".format(eles)
        return tsr

    def __iter__(self):
        return self

    def __next__(self):
        """
        :return
            (index, value)
        """
        if self.pos < len(self.list):
            index = self.pos
            result = index, self.list[self.pos]
            self.pos += 1
            return result
        else:
            self.pos = 0
            raise StopIteration

    def __lt__(self, other):
        this = int(self.section)
        return True if this < int(other.section) else False

    def __gt__(self, other):
        this = int(self.section)
        return False if this < int(other.section) else True

    def __le__(self, other):
        this = int(self.section)
        return True if this <= int(other.section) else False
        pass

    def __ge__(self, other):
        this = int(self.section)
        return False if this <= int(other.section) else True
        pass

    def __contains__(self, item):
        return item in self.list
        pass


class ContainerBase(object):
    def add_ip(self):  pass
    def set_ip_sustain_check_enable(self): pass
    def remove_ipstate(self): pass
    def remove_section_ipstates(self): pass
    def get_all_ipates(self): pass
    

class ContainerInList(ContainerBase):
    """
    纵向容器, 容纳横向容器
    """

    def __init__(self, current_section=None):
        """ """
        self.list: List[Co_ntainer] = []
        self.current_section = current_section  # 当前的字段
        self._pos = 0
        self.ip_dict = {}  # {'ip': ipoat}
        self.curr_sec_cos = []  # 当前字段的 co对象
        self.second_sec_cos = []  #
        
    def reset_data(self):
        self.list = []
        self._pos = 0
        self.ip_dict = {}
        self.curr_sec_cos = []
        self.second_sec_cos = []

    def get_all_ipates(self):
        return list(self.ip_dict.values())
    
    def is_ip_exist(self, ip):
        if isinstance(ip, IpState):
            logger.debug("ip type is IpSate, {}".format(ip.ip))
            return  ip.ip in self.ip_dict
        return ip in self.ip_dict

    def has_section(self, section: str):
        for ip in  self.ip_dict.keys():
            if IpState(ip).section() == section:
                return True
        return False

    def _get_section_cos(self, section):
        s_cos = []
        for co in self.list:
            if co.section == section:
                s_cos.append(co)
        return s_cos

    
    def update_not_online(self):
        set_second = set(self.second_sec_cos)
        set_last = set()
        for co in self.curr_sec_cos:
            for _, ipoat in co:
                set_last.add(ipoat)
        set2 = set_last - set_second
        if set2:
            for ipoat in set2:
                ipoat.set_invalid()



    def add_ip(self, ip: Union[str, IpState]):
        """
         添加一个ipoat对象
         return : x, y, obj
         x : self.list 的对象的索引, 也对应一行的位置
         y: 对应元素在横向容器的位置
         obj: 返回横向元素, 也就是x索引对应的元素(Container)
        """

        if not isinstance(ip, (str, IpState)):
            return None

        if self.is_ip_exist(ip):
            # 先找出来,而不是创建一个新的
            ipstr = ip.ip if isinstance(ip, IpState) else ip
            ipoat: IpState = self.ip_dict.get(ipstr)
            ipoat.set_exists()
            self.second_sec_cos.append(ipoat)
            return None
        else:
            if isinstance(ip, IpState):
                ipoat = ip
                ip = ipoat.ip
            else:
                ipoat = IpState(ip)
                ipoat.set_new()

        self.second_sec_cos.append(ipoat)

        # 加入字典
        self.ip_dict[ip] = ipoat

        # 根据ipoat对象的字段找到一个co对象
        for co_ in self.list:
            if co_.section == ipoat.section() and not co_.isfull():
                y = co_.add(ipoat)
                x = self.list.index(co_)
                return x, y, co_
        else:
            new_co = Co_ntainer()
            new_co.set_section(ipoat.section())
            new_co.add(ipoat)
            x = self._add_co(new_co)
            return x, 0, new_co   # 如果返回的元祖第二个元素的值为0 则说明 又新加了一行

    def _add_co(self, co: Co_ntainer):
        # 如果list为空,直接插入
        if self.is_empty():
            self.list.append(co)
            self.curr_sec_cos.append(co)
            return 0

            # 判断是否为当前的字段容器
        # 如果是则放在第一位
        if co.section == self.current_section:
            self.list.insert(0, co)
            self.curr_sec_cos.append(co)
            return 0
        else:
            # 不是当前的字段容器
            # 找到相同字段的容器,放在它的前面
            b, pos = self.is_container_section(co.section)
            if b:
                self.list.insert(pos, co)
                return pos
            else:
                # 列表里面没有相同字段的容器
                # 放比它小的容器前面
                for obj_ in self.list:
                    if obj_.section == self.current_section:
                        continue
                    else:
                        if co <= obj_:
                            index_ = self.list.index(obj_)
                            self.list.insert(index_, co)
                            return index_
                else:
                    # 没有知道比它小的,放在最后
                    self.list.append(co)
                    return self.list.index(co)

    def is_empty(self):
        return True if len(self.list) == 0 else False

    def is_firt_equal_section(self, false=False):
        """
         如果列表第一个元素的字段为当前字段,则返回True
         否则返回False
         如果列表为空,返回None
        """

        if self.is_empty(): return None
        if self.list[0].section == self.current_section:
            return True
        else:
            return false

    def is_current_section(self, co: Co_ntainer):
        return True if self.current_section == co.section else False

    def is_container_section(self, section: str):
        for co in self.list:
            if co.section == section:
                return True, self.list.index(co)
        return False, False

    def set_ip_sustain_check_enable(self, ip_str: str, b_status=True):
        """
        更改ip的状态,让其不在线
        
        """
        # 查找IP
        ip_object: IpState = self.ip_dict.get(ip_str, None)
        if ip_object:
            if b_status:
                ip_object.set_unknow()
            else:
                ip_object.set_invalid()
                
    def set_ip_sustain_check_enable_s(self, ipate_list: list, b_status = True):
        
        for ipo in ipate_list:
            self.set_ip_sustain_check_enable(ipo.ip, b_status)
        
    def remove_ipstate(self, ipstr: str):
        """
        移除当前IP
        
        """
        ipsate = IpState(ipstr)
        for co in self.list:
            if co.section != ipsate.section():
                continue
            
            for _, ipst in co:
                if isinstance(ipst, IpState): pass
                
                if ipst.is_ip_equal(ipsate):
                    co.list.remove(ipst)
                    # ip_dict 里面也删除ipstt
                    self.ip_dict.pop(ipst.ip)
                    return ipst
                        
    
    def remove_section_ipstates(self, ipstr: str):
        """
        移除当前ip字段的所有IP
        
        """
        ipsate = IpState(ipstr)
        ipsate_list = []
        for co in self.list:
            if co.section != ipsate.section():
                continue
                
            self.list.remove(co)
            # ip_dict 里面也删除ipstt
            for _, ipst in co:
                if isinstance(ipst, IpState): pass
                self.ip_dict.pop(ipst.ip)
                logger.info("remove ipate:{}".format(ipst.ip))
            ipsate_list.extend(co.list)
        return ipsate_list
            
            
    def _get_equal_section_ipstate_list(self, ipstr: str):
        """
        获得给定ip,相同的网段的IP
        :return
            list: [ipate]
        """
        ipate = IpState(ipstr)
        ipate_list = []
        for co in self.list:
            if co.section != ipate.section():
                continue
                
            ipate_list.extend(co.list)
        
        return ipate_list
    
    
    def get_equal_section_ipstr_list(self, ipstr: str):
        """
        元素为 字符串
        """
        return [ipo.ip for ipo in self._get_equal_section_ipstate_list(ipstr)]
    

    def __iter__(self):
        return self
        pass

    def __next__(self):
        if self._pos < len(self.list):
            index = self._pos
            result = index, self.list[self._pos]
            self._pos += 1
            return result
        else:
            self._pos = 0
            raise StopIteration

    def __str__(self):
        str_list = []
        for obj in self.list:
            str_list.append(str(obj))
        tsr_all = '\r\n'.join(str_list)
        return tsr_all


class Struct:
    def __init__(self, section: str):
        self.section = section
        self.size = 10
        self.list = []
        
    # def is_ipstr_exists(self, ipstr:str):
    #     for ipo in self.list:
    #         if ipo.ip == ipstr:
    #             return True
    #     return False
    
    def get_online_ipo_s(self):
        return [ ipo for ipo in self.list if ipo.is_online()]
    
    def get_ipo(self, ipstr: str) -> IpState:
        for ipo in self.list:
            if ipo.ip == ipstr:
                return ipo
        return False
     
    def append(self, ipo: IpState):
        # 如果list里面已经存在此ipo,只更新其状态
        if ipo_find := self.get_ipo(ipo.ip):
            # todo use raise
            if isinstance(ipo_find, IpState): pass
            ipo_find.update_state()
            return
        # 如果是一个新的ipo对象,就只添加
        self.list.append(ipo)
        
    def sort(self):
        # todo IpState 实现比大小
        self.list.sort(key=lambda ipo: int(ipo.tail()))
        pass
    
    
    def remove(self, ipo):
        self.list.remove(ipo)
    
    def split_size(self):
        """
        :return [
        [ipo...],
        [],
        []...
        ]
        
        """
        
        List = []
        if len(self) <= self.size :
            List.append(self.list)
            return List
        
        number_split = len(self) / self.size
        number_split = int(number_split)
        # print("number_split: {}".format(number_split))
        if len(self) % self.size != 0:
            number_split += 1
            
        assert number_split >= 1
        
        slice = 0
        for _ in range(number_split):
            # 切片 元素<=10个
            a_slice = self.list[slice: slice+self.size]
            slice += self.size
            List.append(a_slice )
        
        return List
    
    def str_print(self):
        return "\n".join([ipo.ip for ipo in self.list])
    
    def str_print_after_split(self):
        newlistlist = self.split_size()
        outstr = ""
        for newlist in newlistlist:
            tr = ",  ".join([ipo.ip for ipo in newlist])
            outstr += tr
            outstr += '\n'
        return outstr
        
            
    
    def __len__(self):
        return len(self.list)
    
    
    def __iter__(self):
        return iter(self.list)
    
    
class ContainerTableData(ContainerBase):
    
    def __init__(self):
        
        self.List = [
            # Struct()....
        ]
        # self.already_append_ipo = []
        self.sustain_add = False
        
    def starting_add(self, section:str):
        self.sustain_add = True
        self.cache_structs = []
    
 
    
    def ending_add(self):
        self.sustain_add = False
        
        # 把之前在线的ipo,现在又不在线的ipo的状态设置为invalid
        # if hasattr(self, "cache_struct") and self.cache_structs:
        if self.cache_structs:
            # 存在这个缓存
            struct: Struct = self.current_struct()
            cache_ipstr_list = [ipo.ip for ipo in self.cache_structs]
            for ipo in struct.get_online_ipo_s():
                # todo IpState 实现 __str__
                if ipo.ip not in cache_ipstr_list:
                    ipo.set_invalid()
        
    def current_struct(self):
        return self.List[0]
        
        
    def _get_struct_from_section(self, section: str) -> Struct:
        for struct in self.List:
            if struct.section == section:
                return struct
        return None
        
        
    
    def add_ip(self, ipo: IpState):
        """
        self.cache_structs: [ ipo ....]
        
        """
        struct : Struct = self._get_struct_from_section(ipo.section())
        
        if self.sustain_add:
            self.cache_structs.append(ipo)
        
        # 如果没有找到同字段的struct
        # 新建一个struct
        if struct is None:
            # new struct
            newStruct = Struct(section=ipo.section())
            newStruct.append(ipo)
            self.List.insert(0, newStruct)
            return
            
        # 找到有同字段的struct
        # 1 把该字段的struct移动到List的顶端
        # 2 排序其它的struct
        
        # todo 优化速度
        struct.append(ipo)
        self.List.sort(key=lambda struct: int(struct.section))
        
        self.List.remove(struct)
        self.List.insert(0, struct)
        
        
    def out_print_data(self):
        """
        最后的展现
        :return
        [
            [ipo....],
            [ipo....],
            .....
        ]
        
        """
        ipoList = []
        for struct in self.List:
            struct.sort()
            list_ = struct.split_size()
            if list_:
                ipoList.extend(list_)
        
        return ipoList
        
        
    def remove_ipo(self, ipstr):
        sec = IpState(ipstr).section()
        struct = self._get_struct_from_section(sec)
        ipo = struct.get_ipo(ipstr)
        if ipo:
            struct.remove(ipo)
            return ipo
        
    def remove_struct_from_section(self, section: str):
        struct = self._get_struct_from_section(section)
        if struct:
            self.List.remove(struct)
            return struct.list
        
        
    def clear(self):
        self.List.clear()
    

    def remove_ipstate(self):
        pass

    def remove_section_ipstates(self):
        pass

    @property
    def ipos(self):
        ipo_list = []
        for struct in self.List:
            ipo_list.extend(struct.list)
        return ipo_list


if __name__ == '__main__':
    pass
